package com.probox.order.api.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.probox.common.core.enums.PayStatusEnum;
import com.probox.common.core.enums.PlatSourceEnum;
import com.probox.common.core.text.UUID;
import com.probox.common.core.utils.StringUtils;
import com.probox.order.api.domain.KeyPairFactory;
import com.probox.order.api.domain.ProfitShareReceiver;
import com.probox.order.api.domain.ProfitShareVo;
import com.probox.order.api.domain.WxRefundVo;
import okhttp3.HttpUrl;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

/**
 * 微信服务商模式支付
 */
public class WechatV3Payment {

    //请求网关
    private static final String url_prex = "https://api.mch.weixin.qq.com/";
    //编码
    private static final String charset = "UTF-8";

    public final static String desc = "购买商品";

    //服务商appId
    public final static String sp_appid = "wx07103c90d3575cd1";

    public final static String appid = "wx07103c90d3575cd1";

    public final static String mchid = "1725766500";

    //服务商商户ID
    public final static String sp_mchid = "1725766500";
    //测试店铺appId
    public static  String sub_appid = "wxa98cb985133a2a89";
    //测试店铺商户号，甘石科技
    //public static  String sub_mchid = "1634792106";
    //测试店铺商户号，菠仕图,默认的店铺
    public static  String sub_default_mchid = "1725766500";
    //服务商的证书序列号，从证书文件中解析获得
    private static String serize_no = "";
    //服务商证书文件
    private static String privateKeyFilePath = "cert/apiclient_cert.p12";
    //服务商V3的私钥
    private static String V3PrivateKey = "WenDudu20250828WenDudu2025082888";

    //微信支付回调
    public static final String notify_url = "https://www.wendudu.com/api/api/order/openWechat/paymentCallBack";

    private static String Wechatpay_Serial = "";

    //平台证书
    private static X509Certificate platformCertificate = null;

    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(WechatV3Payment.class);

    /**
     * 设置证书编号
     * @param sno
     */
    public static void setSerize_no(String sno){
        if (StringUtils.isEmpty(sno)) return;
        if (!sno.equals(serize_no)){
            serize_no = sno;
        }
    }

    /**
     * 微信服务商统一下单
     * @param jsonStr
     * @return
     * @throws Exception
     */
    public static String V3PayPartnerTransactionsJsapi(String jsonStr) throws Exception {
        String body = "";
        //logger.info("微信服务商下单内容：{}",jsonStr);
        String url = "v3/pay/transactions/native";


        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url_prex + url);
        //装填参数
        StringEntity s = new StringEntity(jsonStr, charset);
        s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
        s.setContentType("application/json");
        //设置参数到请求对象中
        httpPost.setEntity(s);
        String post = getToken("POST", HttpUrl.parse(url_prex + url), sp_mchid, privateKeyFilePath, jsonStr);
        //设置header信息
        //指定报文头【Content-type】、【User-Agent】
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);
        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpPost);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
            logger.info("微信服务商下单结果：{}",body);
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
        switch (url) {
            case "v3/pay/transactions/app"://返回APP支付所需的参数
                return JSON.parseObject(body).getString("prepay_id");
            case "v3/pay/partner/transactions/jsapi"://返回JSAPI支付所需的参数
                return JSON.parseObject(body).getString("prepay_id");
            case "v3/pay/transactions/native"://返回native的请求地址
                return JSON.parseObject(body).getString("code_url");
            case "v3/pay/partner/transactions/h5"://返回h5支付的链接
                return JSON.parseObject(body).getString("h5_url");
        }
        return null;
    }

    /**
     * 服务商关闭订单
     * @param jsonStr
     * @param outTradeNo
     * @throws Exception
     */
    public static void V3PayPartnerTransactionsJsapiClose(String jsonStr,String outTradeNo) throws Exception {
        String body = "";
        logger.info("微信服务商关闭订单内容：{}",jsonStr);
        String url = "v3/pay/partner/transactions/out-trade-no/"+outTradeNo+"/close";


        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url_prex + url);
        //装填参数
        StringEntity s = new StringEntity(jsonStr, charset);
        s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
        s.setContentType("application/json");
        //设置参数到请求对象中
        httpPost.setEntity(s);
        String post = getToken("POST", HttpUrl.parse(url_prex + url), sp_mchid, privateKeyFilePath, jsonStr);
        //设置header信息
        //指定报文头【Content-type】、【User-Agent】
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);
        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpPost);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
            logger.info("微信服务商关闭订单内容：{}",body);
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
    }

    /*
    获取平台证书
     */
    public static void v3Certificates() throws Exception {
        if (platformCertificate != null){
            return;
        }
        String body = "";
        String url = "https://api.mch.weixin.qq.com/v3/certificates";
        //设置参数到请求对象中
        String post = getToken("GET", HttpUrl.parse(url), sp_mchid, privateKeyFilePath, body);
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Content-type", "application/json");
        httpGet.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpGet.setHeader("Accept", "application/json");
        httpGet.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);

        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpGet);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
            logger.info("查询平台证书的结果：{}", body);
            JSONObject data = JSONObject.parseObject(body);
            JSONArray array = data.getJSONArray("data");
            data = (JSONObject)array.get(0);
            Wechatpay_Serial = data.getString("serial_no");
            String certInfo = verifyNotify2(data.getString("encrypt_certificate"));
            logger.info("certInfo={}",certInfo);
            InputStream inStream = new ByteArrayInputStream(certInfo.getBytes());
            //创建X509工厂类
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            //创建证书对象
            platformCertificate = (X509Certificate)cf.generateCertificate(inStream);
            inStream.close();
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
    }

    public static String verifyNotify2(String body) throws Exception {
        // 获取平台证书序列号
        cn.hutool.json.JSONObject resource = JSONUtil.parseObj(body);
        String cipherText = resource.getStr("ciphertext");
        String nonceStr = resource.getStr("nonce");
        String associatedData = resource.getStr("associated_data");
        AesUtil aesUtil = new AesUtil(V3PrivateKey.getBytes(StandardCharsets.UTF_8));
        // 密文解密
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonceStr.getBytes(StandardCharsets.UTF_8),
                cipherText
        );
    }

    public static String rsaEncryptOAEP(String message)
            throws IllegalBlockSizeException, IOException {
        try {
            if (platformCertificate == null){
                v3Certificates();
            }
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, platformCertificate.getPublicKey());

            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return message;
    }

    /**
     * 添加分账接收方API
     * @param profitShareVo
     * @return
     * @throws Exception
     */
    public static ProfitShareVo V3ProfitsharingReceiversAdd(ProfitShareVo profitShareVo) throws Exception {
        return null;
    }

    /**
     * 请求分账API
     * @param profitShareVo
     * @return
     * @throws Exception
     */
    public static ProfitShareVo V3ProfitsharingOrders(ProfitShareVo profitShareVo) throws Exception {
        return null;
    }

    /**
     * 查询解冻剩余资金的结果
     * @param profitShareVo
     * @return
     * @throws Exception
     */
    public static ProfitShareVo V3ProfitsharingOrdersSearch(ProfitShareVo profitShareVo) throws Exception {
        return null;
    }

    /**
     * 查询最大分账比例API
     * @param profitShareVo
     * @return
     * @throws Exception
     */
    public static ProfitShareVo V3ProfitsharingMerchantConfigs(ProfitShareVo profitShareVo) throws Exception {
        return null;
    }


    /**
     * 解冻剩余资金
     * @param profitShareVo
     * @return
     * @throws Exception
     */
    public static ProfitShareVo V3ProfitsharingOrdersUnfreeze(ProfitShareVo profitShareVo) throws Exception {
        return null;
    }

    /**
     * 微信支付下单
     *
     * @param url                请求地址（只需传入域名之后的路由地址）
     * @param jsonStr            请求体 json字符串 此参数与微信官方文档一致
     * @param mercId             商户ID
     * @param serial_no          证书序列号
     * @param privateKeyFilePath 私钥的路径
     * @return 订单支付的参数
     * @throws Exception
     */
    @Deprecated
    public static String V3PayGet(String url, String jsonStr, String mercId, String serial_no, String privateKeyFilePath) throws Exception {
        String body = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url_prex + url);
        //装填参数
        StringEntity s = new StringEntity(jsonStr, charset);
        s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
                "application/json"));
        //设置参数到请求对象中
        httpPost.setEntity(s);
        String post = getToken("POST", HttpUrl.parse(url_prex + url), mercId, privateKeyFilePath, jsonStr);
        //设置header信息
        //指定报文头【Content-type】、【User-Agent】
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);
        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpPost);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
        switch (url) {
            case "v3/pay/transactions/app"://返回APP支付所需的参数
                return JSON.parseObject(body).getString("prepay_id");
            case "v3/pay/transactions/jsapi"://返回JSAPI支付所需的参数
                return JSON.parseObject(body).getString("prepay_id");
            case "v3/pay/transactions/native"://返回native的请求地址
                return JSON.parseObject(body).getString("code_url");
            case "v3/pay/transactions/h5"://返回h5支付的链接
                return JSON.parseObject(body).getString("h5_url");
        }
        return null;
    }

    /**
     * 生成组装请求头
     *
     * @param method             请求方式
     * @param url                请求地址
     * @param mercId             商户ID
     * @param privateKeyFilePath 私钥路径
     * @param body               请求体
     * @return 组装请求的数据
     * @throws Exception
     */
    static String getToken(String method, HttpUrl url, String mercId, String privateKeyFilePath, String body) throws Exception {
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("UTF-8"), privateKeyFilePath,mercId);
        return "mchid=\"" + mercId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + serize_no + "\","
                + "signature=\"" + signature + "\"";
    }

    /**
     * 生成签名
     *
     * @param message            请求体
     * @param privateKeyFilePath 私钥的路径
     * @return 生成base64位签名信息
     * @throws Exception
     */
    static String sign(byte[] message, String privateKeyFilePath,String mercId) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        KeyPairFactory keyPairFactory = new KeyPairFactory();
        PrivateKey privateKey = keyPairFactory.createPKCS12(privateKeyFilePath,"Tenpay Certificate",mercId).getPrivate();
        sign.initSign(privateKey);
        //sign.initSign(getPrivateKey(privateKeyFilePath));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }



    /**
     * 组装签名加载
     *
     * @param method    请求方式
     * @param url       请求地址
     * @param timestamp 请求时间
     * @param nonceStr  请求随机字符串
     * @param body      请求体
     * @return 组装的字符串
     */
    static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    static PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 构造签名串
     *
     * @param signMessage 待签名的参数
     * @return 构造后带待签名串
     */
    static String buildSignMessage(ArrayList<String> signMessage) {
        if (signMessage == null || signMessage.size() <= 0) {
            return null;
        }
        StringBuilder sbf = new StringBuilder();
        for (String str : signMessage) {
            sbf.append(str).append("\n");
        }
        return sbf.toString();
    }

    /**
     * v3 支付异步通知验证签名
     *
     * @param body 异步通知密文
     * @return 异步通知明文
     * @throws Exception 异常信息
     */
    public static String verifyNotify(String body) throws Exception {
        // 获取平台证书序列号
        cn.hutool.json.JSONObject resultObject = JSONUtil.parseObj(body);
        cn.hutool.json.JSONObject resource = resultObject.getJSONObject("resource");
        String cipherText = resource.getStr("ciphertext");
        String nonceStr = resource.getStr("nonce");
        String associatedData = resource.getStr("associated_data");
        AesUtil aesUtil = new AesUtil(V3PrivateKey.getBytes(StandardCharsets.UTF_8));
        // 密文解密
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonceStr.getBytes(StandardCharsets.UTF_8),
                cipherText
        );
    }

    /**
     * 处理返回对象
     *
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 处理微信异步回调
     *
     * @param request
     * @param response
     */
    public static String notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> map = new HashMap<>(12);
        String result = readData(request);
        // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
        String plainText = verifyNotify(result);
        if (StrUtil.isNotEmpty(plainText)) {
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "SUCCESS");
        } else {
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "签名错误");
        }
        response.setHeader("Content-type", ContentType.JSON.toString());
        response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
        response.flushBuffer();
        String out_trade_no = JSON.parseObject(plainText).getString("out_trade_no");
        return out_trade_no;
    }

    /**
     * 微信调起支付参数
     * 返回参数如有不理解 请访问微信官方文档
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_1_4.shtml
     *
     * @param prepayId           微信下单返回的prepay_id
     * @param appId              应用ID(appid)
     * @return 当前调起支付所需的参数
     * @throws Exception
     */
    public static JSONObject WxTuneUp(String prepayId, String appId) throws Exception {
        String time = System.currentTimeMillis() / 1000 + "";
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String packageStr = "prepay_id=" + prepayId;
        ArrayList<String> list = new ArrayList<>();
        list.add(appId);
        list.add(time);
        list.add(nonceStr);
        list.add(packageStr);
        //加载签名
        String packageSign = sign(buildSignMessage(list).getBytes(), privateKeyFilePath,sp_mchid);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", appId);
        jsonObject.put("timeStamp", time);
        jsonObject.put("nonceStr", nonceStr);
        jsonObject.put("packages", packageStr);
        jsonObject.put("signType", "RSA");
        jsonObject.put("paySign", packageSign);
        jsonObject.put("payStatus", PayStatusEnum.unpaid.getKey());
        return jsonObject;
    }


    /**
     * V3申请退款
     * @param jsonStr
     * @return
     */
    public static WxRefundVo V3ApplyRefund(String jsonStr) throws Exception {
        String body = "";
        logger.info("微信退款内容：{}",jsonStr);
        String url = "v3/refund/domestic/refunds";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url_prex + url);
        //装填参数
        StringEntity s = new StringEntity(jsonStr, charset);
        s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
        s.setContentType("application/json");
        //设置参数到请求对象中
        httpPost.setEntity(s);
        String post = getToken("POST", HttpUrl.parse(url_prex + url), sp_mchid, privateKeyFilePath, jsonStr);
        //设置header信息
        //指定报文头【Content-type】、【User-Agent】
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);
        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpPost);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
            logger.info("微信服务商退款结果：{}",body);
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
        return JSON.parseObject(body, WxRefundVo.class);
    }
}
